/*
 * Copyright 2012 Gregor Schauer
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package at.schauer.gregor.vienna.datastore.database;

import at.schauer.gregor.vienna.context.DefaultViennaContext;
import at.schauer.gregor.vienna.datastore.database.querybuilder.NamedQueryBuilder;
import at.schauer.gregor.vienna.identifier.IdentifierGenerator;
import at.schauer.gregor.vienna.identifier.IncrementGenerator;
import at.schauer.gregor.vienna.metadata.MetadataProvider;
import at.schauer.gregor.vienna.serializer.DefaultSerializer;
import at.schauer.gregor.vienna.serializer.Serializer;
import at.schauer.gregor.vienna.serializer.builder.DefaultConstructorBuilder;
import at.schauer.gregor.vienna.serializer.builder.ObjectBuilder;
import at.schauer.gregor.vienna.serializer.decomposer.DirectFieldDecomposer;
import at.schauer.gregor.vienna.serializer.decomposer.ObjectDecomposer;
import at.schauer.gregor.vienna.serializer.populator.DirectFieldPopulator;
import at.schauer.gregor.vienna.serializer.populator.ObjectPopulator;
import at.schauer.gregor.vienna.test.Student;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.util.Collections;

import static org.junit.Assert.assertEquals;

/**
 * @author Gregor Schauer
 */
public class QueryBuilderDatabaseDataStoreIT {
	@Test
	public void test() throws IOException {
		DefaultSerializer<Object> serializer = new DefaultSerializer<Object>();
		serializer.setBuilderMap(Collections.<Class<?>, ObjectBuilder<?>>singletonMap(Student.class, new DefaultConstructorBuilder<Object>()));
		serializer.setPopulatorMap(Collections.<Class<?>, ObjectPopulator<?>>singletonMap(Student.class, new DirectFieldPopulator<Object>()));
		serializer.setDecomposerMap(Collections.<Class<?>, ObjectDecomposer<?>>singletonMap(Student.class, new DirectFieldDecomposer<Object>()));

		DefaultViennaContext ctx = new DefaultViennaContext();
		ctx.setMetadataProviderMap(Collections.<Class<?>, MetadataProvider<?>>singletonMap(Student.class, new CustomMetadataProvider()));
		ctx.setSerializerMap(Collections.<Class<?>, Serializer<?>>singletonMap(Student.class, serializer));
		ctx.setIdentifierGeneratorMap(Collections.<Class<?>, IdentifierGenerator<?, ?>>singletonMap(Student.class, new IncrementGenerator<Object>()));

		EmbeddedDatabaseBuilder databaseBuilder = new EmbeddedDatabaseBuilder();
		databaseBuilder.setType(EmbeddedDatabaseType.HSQL);
		EmbeddedDatabase dataSource = databaseBuilder.build();
		JdbcTemplate template = new JdbcTemplate(dataSource);

		NamedQueryBuilder queryBuilder = new NamedQueryBuilder(Student.class);
		QueryBuilderDatabaseDataStore<Object> dataStore = new QueryBuilderDatabaseDataStore<Object>(ctx, template, queryBuilder);


		Student student = new Student(null, "James Doe");
		Object save = dataStore.save(student);
		assertEquals(1L, save);
		assertEquals(1, dataStore.list(student.getClass()).size());

		int rows = template.queryForInt("SELECT COUNT(*) FROM \"Student\"");
		assertEquals(1, rows);

		student.setPassword("top_secret");
		assertEquals(1L, dataStore.save(student));

		Student load = dataStore.load(student.getClass(), student.getId());
		assertEquals(student, load);

		dataStore.delete(student);
		assertEquals(0, dataStore.list(student.getClass()).size());
	}

	static class CustomMetadataProvider implements MetadataProvider<Student> {
		private IdentifierGenerator<Long, Student> identifierGenerator = new IncrementGenerator<Student>();

		@Override
		public Object getIdentifier(@Nonnull Student obj) {
			return obj.getId();
		}

		@Override
		public void setIdentifier(@Nonnull Student obj, @Nullable Object identifier) {
			try {
				FieldUtils.writeField(obj, "id", identifier, true);
			} catch (IllegalAccessException e) {
				throw new RuntimeException(e);
			}
		}

		@Override
		public String getIdentifierName(@Nonnull Class<? extends Student> clazz) {
			return "id";
		}

		@Override
		public boolean isTransientType(@Nonnull Class<? extends Student> clazz) {
			return false;
		}

		@Override
		public <M extends AccessibleObject & Member> boolean isTransient(@Nonnull M member) {
			return Modifier.isTransient(member.getModifiers());
		}

		@Nonnull
		@Override
		public String getEntityName(@Nonnull Class<? extends Student> clazz) {
			return clazz.getSimpleName();
		}
	}
}
